package com.cxx.purchasecharge.component.bean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import com.cxx.purchasecharge.component.exception.ApplicationException;
import com.cxx.purchasecharge.component.utils.ComponentConstants;
import com.cxx.purchasecharge.core.model.AuthorityResource;
import com.cxx.purchasecharge.core.model.persistence.Authority;
import com.cxx.purchasecharge.dal.AuthorityDao;
import com.cxx.purchasecharge.dal.AuthorityResourceDao;

public class PCApplicationContext implements Serializable, InitializingBean
{
    private static final long serialVersionUID = 1L;
    private static final Logger logger = Logger.getLogger (PCApplicationContext.class);

    private LoginUser mockLoginUser;
    private Map <String, LoginUser> loginUserMap = new HashMap <String, LoginUser> ();
    private Map <RequestMatcher, Collection <ConfigAttribute>> securityResourceMap = new HashMap <RequestMatcher, Collection <ConfigAttribute>> ();
    
    private AuthorityDao authorityDao;
    private AuthorityResourceDao authorityResourceDao;

    public LoginUser getLoginUser (String userId)
    {
        if (CollectionUtils.isEmpty (loginUserMap))
        {
            return null;
        }
        return loginUserMap.get (userId);
    }

    public LoginUser getLoginUser (HttpServletRequest request) throws Exception
    {
        LoginUser loginUser = null;
        if (null != request)
        {
            Object obj = request.getSession (false).getAttribute (ComponentConstants.LOGIN_USER);
            if (null != obj)
            {
                loginUser = (LoginUser) obj;
            }
            else
            {
                loginUser = getMockLoginUser ();
            }
        }
        if (null == loginUser)
        {
            logger.warn ("Get Login User failed");
            throw new ApplicationException ("Get Login User failed");
        }
        return loginUser;
    }

    public void putLoginUser (LoginUser loginUser)
    {
        if (null != loginUser && StringUtils.isNotBlank (loginUser.getUserId ()))
        {
            loginUserMap.put (loginUser.getUserId (), loginUser);
        }
    }

    public LoginUser removeLoginUser (String userId)
    {
        if (StringUtils.isNotBlank (userId))
        {
            return loginUserMap.remove (userId);
        }
        return null;
    }

    public LoginUser getMockLoginUser ()
    {
        return mockLoginUser;
    }

    public void setMockLoginUser (LoginUser mockLoginUser)
    {
        this.mockLoginUser = mockLoginUser;
    }

    public Map <String, LoginUser> getLoginUserMap ()
    {
        return loginUserMap;
    }

    public Map<RequestMatcher, Collection<ConfigAttribute>> getSecurityResourceMap(boolean isRetrieveLatest)
    {
        if(securityResourceMap.isEmpty() || isRetrieveLatest) 
        {
            loadAllResource ();
        }
        return securityResourceMap;
    }

    public void setAuthorityDao(AuthorityDao authorityDao)
    {
        this.authorityDao = authorityDao;
    }

    public void setAuthorityResourceDao(AuthorityResourceDao authorityResourceDao)
    {
        this.authorityResourceDao = authorityResourceDao;
    }

    private void loadAllResource ()
    {
        List <Authority> authorities = (List <Authority>) authorityDao.findAll ();
        /*
         * 应当是资源为key， 权限为value。 资源通常为url(.do/.html)， 权限就是角色能访问的资源。
         * 一个资源可以由多个权限来访问。
         */
        securityResourceMap = new HashMap <RequestMatcher, Collection <ConfigAttribute>> ();
        for (Authority auth : authorities)
        {
            ConfigAttribute configAttribute = new SecurityConfig (auth.getName ());
            List <AuthorityResource> authorityResources = authorityResourceDao.findByAuthorityName (auth.getName ());
            for (AuthorityResource res : authorityResources)
            {
                String url = res.getResourceValue ();
                RequestMatcher requestMatcher = new AntPathRequestMatcher (url);

                /*
                 * 判断资源和权限的对应关系，如果已经存在相关的资源url，则要通过该url为key提取出权限集合， 将权限增加到权限集合中。
                 */
                if (securityResourceMap.containsKey (requestMatcher))
                {
                    Collection <ConfigAttribute> atts = securityResourceMap.get (requestMatcher);
                    atts.add (configAttribute);
                    securityResourceMap.put (requestMatcher, atts);
                }
                else
                {
                    Collection <ConfigAttribute> atts = new ArrayList <ConfigAttribute> ();
                    atts.add (configAttribute);
                    securityResourceMap.put (requestMatcher, atts);
                }
            }
        }
    }
    
    @Override
    public void afterPropertiesSet () throws Exception
    {
        Assert.notNull (this.authorityDao, "a AuthorityDao is required");
        Assert.notNull (this.authorityResourceDao, "a AuthorityResourceDao is required");
    }

}
